<?php

namespace app\utils\base\helper;

class Yifang_download
{
    /**
     * 清除xss
     * @param $filePath
     * @return array|mixed|string
     */
    public function clearXss($filePath){
        $filePath=security()->clean($filePath);
        $filePath = clearXss($filePath);
        return $filePath;
    }

    /**
     * 下载文件
     */
    public function downloadFile($filePath){
        if ("https://" != substr($filePath, 0, 8) && "http://" != substr($filePath, 0, 7)) {
            $filePath=publicPath().$filePath;
        }
        $filePath=$this->clearXss($filePath);
        if(is_file($filePath)){
            $fp=fopen($filePath,"r");
            //取得文件大小
            $fileSize=filesize($filePath);

            header("Content-type:application/octet-stream");//设定header头为下载
            header("Accept-Ranges:bytes");
            header("Content-Disposition: attachment; filename=12341");//文件名

            ob_end_clean();//缓冲区结束
            ob_implicit_flush();//强制每当有输出的时候,即刻把输出发送到浏览器
            header('X-Accel-Buffering: no'); // 不缓冲数据

            $buffer=1024* 1000;
            $bufferCount=0;
            if (isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])) {
                //切割字符串
                $range = explode('-', substr($_SERVER['HTTP_RANGE'], 6));
                $diff=$range[0]-$range[1];
                fseek($fp, $range[0]);//移动文件指针到range上
                header('HTTP/1.1 206 Partial Content');
                header("Content-Range: bytes {$diff}/{$fileSize}");
                if($range[1] >0 ){
                    $fileSize=$range[1];//只获取range[1]的数量
                }
                $contentLength=$fileSize - $range[0];
                header("content-length:" . $contentLength);
                while (!feof($fp) && $contentLength - $bufferCount > 0) {//循环读取文件数据
                    //避免多读取
                    $buffer>($contentLength-$bufferCount)&&$buffer=$contentLength-$bufferCount;
                    $data = fread($fp, $buffer);
                    $bufferCount += $buffer;
                    echo $data;//输出文件
                    sleep(1);
                }
            } else {
                header("Accept-Length:" . $fileSize);//响应大小
                while(!feof($fp) && $fileSize-$bufferCount>0){//循环读取文件数据
                    $data=fread($fp,$buffer);
                    $bufferCount+=$buffer;
                    echo $data;//输出文件
                    sleep(1);//增加了一个sleep
                }
            }
            fclose($fp);
        }else{
            http_response_code(404);
            echo "文件不存在";
        }
    }

    /**
     * 单个下载
     */
    public function downloadUrl($filePath,$nums=0,$fileSize="0",$buffer=1024 * 1024){
        if ("https://" != substr($filePath, 0, 8) && "http://" != substr($filePath, 0, 7)) {
            $filePath=publicPath().$filePath;
        }
        $filePath=$this->clearXss($filePath);
        $fileSize=$fileSize?$fileSize:fileInfo($filePath, "content-length",0);
        $start = $nums * $buffer + $nums;
        $end = $start + $buffer;
        $isOver=0;
        if($start < ($fileSize-1)){
            $startNowTime=app("yifangTime")::getMsecTime();
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $filePath);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_FAILONERROR, false);
            curl_setopt($ch,CURLOPT_TIMEOUT, 0);// 单位 秒，超时限制。

            if($end > $fileSize){
                $end=$fileSize-1;
                $isOver=1;
            }
            curl_setopt($ch, CURLOPT_RANGE, $start . '-' . $end);
            header('Content-Range:bytes ' . $start . '-' . $end);//告诉前端当前请求范围
            $content = curl_exec($ch);
            $info = curl_getinfo($ch);
            $endNowTime=app("yifangTime")::getMsecTime();

            $diffTime=($endNowTime-$startNowTime)/1000;
            if($diffTime > 0){
                $bytePerSecond=intval($buffer/$diffTime);
                $rate=app("yifangFormat")::byte($bytePerSecond);
            }else{
                $rate=app("yifangFormat")::byte($buffer);
            }

            $data=array();
            $data["end"]=$end;
            $data["fileSize"]=$fileSize;
            $data["progress"]=app("yifangFormat")->progress($end,$fileSize);
            $data["rate"]=$rate;
            if($isOver){
                $data["isOver"]=1;
                $data["content"]=base64_encode($content);
            }else{
                if($info['http_code']=="206"){
                    $data["isOver"]=0;
                    $data["content"]=base64_encode($content);
                }else{
                    $data["isOver"]=1;
                }
            }
            return $data;
        }else{
            $data=array();
            $data["isOver"]=1;
            $data["fileSize"]=$fileSize;
            $data["end"]=$end;
            $data["rate"]="---";
            $data["progress"]=app("yifangFormat")->progress($end,$fileSize);
            return $data;
        }
    }

    /**
     * 多线程下载
     */
    public function multiDownload($filePath,$savePath=""){
        if ("https://" != substr($filePath, 0, 8) && "http://" != substr($filePath, 0, 7)) {
            $filePath=publicPath().$filePath;
        }
        $filePath=$this->clearXss($filePath);
        $fileSize=fileInfo($filePath, "content-length",0);

        //开启多线程下载
        $mh = curl_multi_init();
        $count = 5;//n个线程
        $handle = [];//n线程数组
        $data = [];//数据分段数组
        $fileData = ceil($fileSize / $count);

        for ($i = 0; $i < $count; $i++) {
            $ch = curl_init();
            //判断是否读取数量大于剩余数量
            if ($fileData > ($fileSize-($i * $fileData))) {
                $headerData = [
                    "Range:bytes=" . $i * $fileData . "-" . ($fileSize)
                ];
            }else{
                $headerData = [
                    "Range:bytes=" . $i * $fileData . "-" .(($i+1)*$fileData)
                ];
            }
            curl_setopt($ch, CURLOPT_URL, $filePath);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_FAILONERROR, false);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // return don't print
            curl_setopt($ch, CURLOPT_TIMEOUT, 0);
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 302 redirect
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headerData);
            curl_setopt($ch, CURLOPT_MAXREDIRS, 7);
            curl_multi_add_handle($mh, $ch); // 把 curl resource 放进 multi curl handler 里
            $handle[$i] = $ch;
        }
        $active = null;

        do {
            //同时执行多线程,直到全部完成或超时
            $mrc = curl_multi_exec($mh, $active);
        } while ($active);

        for ($i = 0; $i < $count; $i++) {
            $data[$i] = curl_multi_getcontent($handle[$i]);
            curl_multi_remove_handle($mh, $handle[$i]);
        }
        curl_multi_close($mh);
        if($savePath){
            $file = implode('',$data);//组合成一个文件
            file_put_contents($savePath,$file);
        }
    }
}